<!--
@license
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<script>

  /**
   * Automatically extend using objects referenced in `behaviors` array.
   *
   *     someBehaviorObject = {
   *       accessors: {
   *        value: {type: Number, observer: '_numberChanged'}
   *       },
   *       observers: [
   *         // ...
   *       ],
   *       ready: function() {
   *         // called before prototoype's ready
   *       },
   *       _numberChanged: function() {}
   *     };
   *
   *     Polymer({
   *
   *       behaviors: [
   *         someBehaviorObject
   *       ]
   *
   *       ...
   *
   *     });
   *
   * @class base feature: behaviors
   */

  Polymer.Base._addFeature({

    /**
     * Array of objects to extend this prototype with.
     *
     * Each entry in the array may specify either a behavior object or array
     * of behaviors.
     *
     * Each behavior object may define lifecycle callbacks, `properties`,
     * `hostAttributes`, `observers` and `listeners`.
     *
     * Lifecycle callbacks will be called for each behavior in the order given
     * in the `behaviors` array, followed by the callback on the prototype.
     * Additionally, any non-lifecycle functions on the behavior object are
     * mixed into the base prototype, such that same-named functions on the
     * prototype take precedence, followed by later behaviors over earlier
     * behaviors.
     */
    behaviors: [],

    _desugarBehaviors: function() {
      if (this.behaviors.length) {
        this.behaviors = this._desugarSomeBehaviors(this.behaviors);
      }
    },

    _desugarSomeBehaviors: function(behaviors) {
      var behaviorSet = [];
      // iteration 1
      behaviors = this._flattenBehaviorsList(behaviors);
      // iteration 2
      // traverse the behaviors in _reverse_ order (youngest first) because
      // `_mixinBehavior` has _first property wins_ behavior, this is done
      // to optimize # of calls to `_copyOwnProperty`
      for (var i=behaviors.length-1; i>=0; i--) {
        var b = behaviors[i];
        if (behaviorSet.indexOf(b) === -1) {
          this._mixinBehavior(b);
          behaviorSet.unshift(b);
        }
      }
      return behaviorSet;
    },

    _flattenBehaviorsList: function(behaviors) {
      var flat = [];
      for (var i=0; i < behaviors.length; i++) {
        var b = behaviors[i];
        if (b instanceof Array) {
          flat = flat.concat(this._flattenBehaviorsList(b));
        }
        // filter out null entries so other iterators don't need to check
        else if (b) {
          flat.push(b);
        } else {
          this._warn(this._logf('_flattenBehaviorsList', 'behavior is null, check for missing or 404 import'));
        }
      }
      return flat;
    },

    _mixinBehavior: function(b) {
      var n$ = Object.getOwnPropertyNames(b);
      var useAssignment = b._noAccessors;
      for (var i=0, n; (i<n$.length) && (n=n$[i]); i++) {
        if (!Polymer.Base._behaviorProperties[n] && !this.hasOwnProperty(n)) {
          if (useAssignment) {
            this[n] = b[n];
          } else {
            this.copyOwnProperty(n, b, this);
          }
        }
      }
    },

    _prepBehaviors: function() {
      this._prepFlattenedBehaviors(this.behaviors);
    },

    _prepFlattenedBehaviors: function(behaviors) {
      // iteration 3
      // `_prepBehavior` goes in natural order
      // otherwise, it's a tricky detail for implementors of `_prepBehavior`
      for (var i=0, l=behaviors.length; i<l; i++) {
        this._prepBehavior(behaviors[i]);
      }
      // prep our prototype-as-behavior
      this._prepBehavior(this);
    },

    _marshalBehaviors: function() {
      for (var i=0; i < this.behaviors.length; i++) {
        this._marshalBehavior(this.behaviors[i]);
      }
      this._marshalBehavior(this);
    }

  });

  // special properties on behaviors are not mixed in and are instead
  // either processed specially (e.g. listeners, properties) or available
  // for calling via doBehavior (e.g. created, ready)
  Polymer.Base._behaviorProperties = {
    hostAttributes: true,
    beforeRegister: true,
    registered: true,
    properties: true,
    observers: true,
    listeners: true,
    created: true,
    attached: true,
    detached: true,
    attributeChanged: true,
    ready: true,
    _noAccessors: true
  }

</script>
